package com.fred.colorpicker.indicator

import android.content.Context
import android.content.SharedPreferences
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF
import android.graphics.RectF
import com.fred.colorpicker.GraySeekBarBean
import com.fred.colorpicker.dip
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow

abstract class Indicator(val context: Context, private val onChange:((selectColor:Int,
                                                                     selectHsv:FloatArray) ->Unit)?) {

    private var isGray = false
    protected val paddingSize by lazy {
        context.dip(4F)
    }
    protected val currentPoint by lazy { PointF(-1f,-1f) }
    var isSelected = false
        set(value) {
            val old = field
            if (field != value) {
                field = value
                if (old && isGray) {
                    saveGrayPosition()
                }
            }
        }
    protected var defaultHRatio = 0.55F
    protected var defaultVRatio = 0.85F
    private var defaultGrayRatio = 0.7F
    protected var defaultColor = -2687093
    protected var defaultSaturation = 0.23F
    protected var defaultBrightness = 0.77F
    private var width = 0
    private var height = 0

    /**
     * 标记是否绘制中心
     */
    var isDrawCentre = true

    fun initPosition(w:Int, h:Int){
        val settings: SharedPreferences = context.getSharedPreferences(
            "colorPicker",
            Context.MODE_PRIVATE
        )
        width = w
        height = h
        val name = javaClass.name
        val hRatio = settings.getFloat("${name}hRatio",defaultHRatio)
        val vRatio = settings.getFloat("${name}vRation",defaultVRatio)
        currentPoint.set(hRatio*w,vRatio*h)
        change(currentPoint.x, currentPoint.y)
    }

    fun savePosition() {
        if (width > 0 && height > 0) {
            val settings: SharedPreferences =
                context.getSharedPreferences("colorPicker", Context.MODE_PRIVATE)
            val edit = settings.edit()
            val name = javaClass.name
            edit.putFloat("${name}hRatio", currentPoint.x / width)
            edit.putFloat("${name}vRation", currentPoint.y / height)
            edit.apply()
        }
    }

    fun initGrayPosition(w:Int, y: Float) {
        val settings: SharedPreferences = context.getSharedPreferences(
            "colorPicker",
            Context.MODE_PRIVATE
        )
        width = w
        val name = javaClass.name
        val ratio = settings.getFloat("${name}grayRatio",defaultGrayRatio)
        currentPoint.x = ratio*w
        currentPoint.y = y
        isGray = true
    }

    fun saveGrayPosition() {
        if (width > 0) {
            val settings: SharedPreferences =
                context.getSharedPreferences("colorPicker", Context.MODE_PRIVATE)
            val edit = settings.edit()
            val name = javaClass.name
            edit.putFloat("${name}grayRatio", currentPoint.x / width)
            edit.apply()
        }
    }

    fun getCurrentX():Float = currentPoint.x

    fun change(x: Float, y: Float) {
        if (isSelected) {
            val newY = if (y == -1f) currentPoint.y else y
            val color = getColor(x, newY)
            val hsv = getGrad(x, newY)
            onChange?.invoke(color, hsv)
        }
    }

    private fun getColor(x: Float, y: Float):Int{
        return if (width > 0 && height > 0) {
            val hsv = FloatArray(3)
            hsv[0] = 360 * min(max(x, 0F), width.toFloat()) / width
            val newY = min(max(y, 0F), height.toFloat())
            hsv[1] = min(newY, height / 2F) / (height / 2)
            hsv[2] = 1 - max(newY - height / 2, 0F) / (height / 2)
            Color.HSVToColor(hsv)
        }else{
            -0
        }
    }

    private fun getGrad(x: Float, y: Float):FloatArray{
        val hsv = FloatArray(3)
        hsv[0] = 360* min(max(x, 0F), width.toFloat()) /width
        hsv[1] = max(min(y, height.toFloat()), 0F) /height
        hsv[2] = 1 - max(min(y, height.toFloat()), 0F) /height
        return hsv
    }

    /**
     * 判断 (x,y)是否在其内部
     * @param x Float
     * @param y Float
     * @return Boolean
     */
    abstract fun isInside(x: Float, y: Float): Boolean

    abstract fun draw(canvas: Canvas, x: Float, y:Float)

    abstract fun minimumWidth(): Int

    abstract fun minimumHeight(): Int

}

class RectIndicator(context: Context, onChange:((selectColor:Int,
                                                 selectHsv:FloatArray) ->Unit)?) : Indicator(context, onChange) {

    init {
        isSelected = false
        defaultHRatio = 0.25F
        defaultVRatio = 0.25F
    }
    private val size = context.dip(13F)
    private val paint by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            strokeWidth = context.dip(3F)
            color = Color.BLACK
        }
    }

    private val paint1 by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            strokeWidth = context.dip(2F)
            color = Color.WHITE
        }
    }
    private val pointPaint by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            color = Color.BLACK
        }
    }
    private val ringRect by lazy {
        RectF()
    }

    override fun isInside(x: Float, y: Float): Boolean {
        val dx = abs(currentPoint.x - x)
        val dy = abs(currentPoint.y - y)
        return dx <= (size + paddingSize) && dy <= (size + paddingSize)
    }

    override fun draw(canvas: Canvas, x: Float, y:Float) {
        if (isSelected) {
            val newX = if (x == -1f) currentPoint.x else x
            val newY = if (y == -1f) currentPoint.y else y
            if (isDrawCentre) {
                val s = size / 3
                canvas.drawLine(newX - s, newY, newX + s, newY, pointPaint)
                canvas.drawLine(newX, newY - s, newX, newY + s, pointPaint)
            }
            paint.alpha = 255
            paint1.alpha = 255
            ringRect.set(newX - size, newY - size, newX + size, newY + size)
            currentPoint.x = newX
            currentPoint.y = newY
        } else {
            paint.alpha = 125
            paint1.alpha = 125
            ringRect.set(currentPoint.x - size, currentPoint.y - size,
                         currentPoint.x + size, currentPoint.y + size)
        }
        canvas.drawRect(ringRect, paint)
        canvas.drawRect(ringRect, paint1)
    }

    override fun minimumWidth() = (2 * size).toInt()

    override fun minimumHeight() = (2 * size).toInt()

}

class RingIndicator(context: Context,onChange:((selectColor:Int,
                                                selectHsv:FloatArray) ->Unit)?) : Indicator(context,onChange) {

    init {
        isSelected = true
        defaultHRatio = 0.55F
        defaultVRatio = 0.85F
    }

    private var radius = context.dip(14F)
    private val paint by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            strokeWidth = context.dip(3F)
            color = Color.BLACK
        }
    }

    private val paint1 by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            strokeWidth = context.dip(2F)
            color = Color.WHITE
        }
    }
    private val ringRect by lazy {
        RectF()
    }
    private val pointPaint by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            strokeWidth = context.dip(1F)
            color = Color.BLACK
        }
    }

    override fun isInside(x: Float, y: Float): Boolean {
        val dx = currentPoint.x - x
        val dy = currentPoint.y - y
        return dx.pow(2) + dy.pow(2) <= (radius + paddingSize).pow(2)
    }

    override fun draw(canvas: Canvas, x: Float, y:Float) {
        val size = radius
        if (isSelected) {
            val newX = if (x == -1f) currentPoint.x else x
            val newY = if (y == -1f) currentPoint.y else y
            if (isDrawCentre) {
                val s = radius / 3
                canvas.drawLine(newX - s, newY, newX + s, newY, pointPaint)
                canvas.drawLine(newX, newY - s, newX, newY + s, pointPaint)
            }
            paint.alpha = 255
            paint1.alpha = 255
            ringRect.set(newX - size, newY - size, newX + size, newY + size)
            currentPoint.x = newX
            currentPoint.y = newY
        } else {
            paint.alpha = 125
            paint1.alpha = 125
            ringRect.set(currentPoint.x - size, currentPoint.y - size,
                         currentPoint.x + size, currentPoint.y + size)
        }
        canvas.drawArc(ringRect, 360F, 360F, false, paint)
        canvas.drawArc(ringRect, 360F, 360F, false, paint1)
    }

    override fun minimumWidth() = (2 * radius).toInt()

    override fun minimumHeight() = (2 * radius).toInt()

}